package owlapps2012.owlToSql.events.p4;

import java.awt.event.ActionEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Set;
import org.apache.log4j.Logger;
import org.protege.editor.owl.model.inference.OWLReasonerManager;
import org.protege.editor.owl.model.inference.ReasonerStatus;
import org.protege.editor.owl.model.inference.ReasonerUtilities;
import org.protege.editor.owl.ui.action.ProtegeOWLAction;
import org.protege.editor.owl.ui.renderer.EventRendering;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.reasoner.InferenceType;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import owlapps2012.owlToSql.base.DynamicAppConfig;
import owlapps2012.owlToSql.base.OwlToSqlTransformationException;
import owlapps2012.owlToSql.base.TemporalExpressionAnalyzer;
import owlapps2012.owlToSql.events.EventsTransformer;
import uk.ac.manchester.cs.owl.owlapi.mansyntaxrenderer.ManchesterOWLSyntaxOWLObjectRendererImpl;

/**
* Author: Piotr Nowara<br>
* piotrnowara@gmail.com<br>
* code made available under Mozilla Public License (http://www.mozilla.org/MPL/MPL-1.1.html)<br>
* copyright 2012, Piotr Nowara<br>
*/
public class EventsToSQLP4Transformer extends ProtegeOWLAction
{

	private static final long serialVersionUID = -1239374784081176791L;
	private static final String configFileName = EventsToSQLP4Transformer.class.getSimpleName().concat(".properties");
	private static final String reportFileSeparator = "^\t";
	private static Logger log = Logger.getLogger(EventsToSQLP4Transformer.class);
	private String jarDirectory;
	private DynamicAppConfig lfToSQLConfig;


	@Override
	public void initialise() throws Exception 
	{
		this.jarDirectory = new File(EventsToSQLP4Transformer.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getPath();
		try 
		{
			this.lfToSQLConfig = new DynamicAppConfig(jarDirectory.concat(File.separator).concat(configFileName));
		} 
		catch (Exception e) 
		{
			throw new Exception("Unable to read configuration file: "+jarDirectory.concat(File.separator).concat(configFileName), e);
		}
	}


	@Override
	public void dispose() throws Exception {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void actionPerformed(ActionEvent e)
	{
		ArrayList<OWLClass> listOfSuccesfullyTransformedCategories = new ArrayList<OWLClass>();
		ArrayList<OWLClass> listOfNotTransformedCategories = new ArrayList<OWLClass>();
		/*
		 * Obtain/create annotation property for writing SQL queries currentOntology:sqlSelect
		 * Find relevant OWL classes (Get all subclasses of Event)
		 * Transform every equivalent class expr into SQL and writing that query into an annotation (removing any existing currentOntology:sqlSelect and andding new ones)
		 * 
		 */
		
		OWLOntology activeOntology =  getOWLModelManager().getActiveOntology();
		OWLAnnotationProperty sqlSelectAP = getOWLDataFactory().getOWLAnnotationProperty(IRI.create(activeOntology.getOntologyID().getOntologyIRI().toString().concat("#sqlSelect")));
		OWLAnnotationProperty backwardValidityAP = getOWLDataFactory().getOWLAnnotationProperty(IRI.create(activeOntology.getOntologyID().getOntologyIRI().toString().concat("#backwardValidity")));
		OWLAnnotationProperty forwardValidityAP = getOWLDataFactory().getOWLAnnotationProperty(IRI.create(activeOntology.getOntologyID().getOntologyIRI().toString().concat("#forwardValidity")));
		OWLReasonerManager reasonerManager = getOWLModelManager().getOWLReasonerManager();
		ReasonerUtilities.warnUserIfReasonerIsNotConfigured(getOWLWorkspace(), reasonerManager);

	
        OWLReasoner reasoner = reasonerManager.getCurrentReasoner();
        reasoner.precomputeInferences(new InferenceType[0]);
        
        OWLClass lastSelectedClass = getOWLWorkspace().getOWLSelectionModel().getLastSelectedClass();
        if (lastSelectedClass != null && reasonerManager.getReasonerStatus() == ReasonerStatus.INITIALIZED) 
        {
            Set<OWLClass> classesToTranslate = reasoner.getSubClasses(lastSelectedClass, false).getFlattened();
            classesToTranslate.add(lastSelectedClass);
            EventsTransformer et;
			try
			{
				et = new EventsTransformer(lfToSQLConfig);
			} 
			catch (Exception e2)
			{
				log.error("Error while loading configuration.", e2);
				return;
			}
            for (OWLClass classToTranslate : classesToTranslate) 
            {
            	if(classToTranslate.isOWLNothing())//owl:Nothing is a subclass of every OWL class
            		continue;
            	
            	log.info("Initiating transformation of the following OWL class: "+classToTranslate);
            	
            	if(!reasoner.isSatisfiable(classToTranslate))
            	{
            		log.warn("Transformation cancelled - cause: unsatisfiable class: "+classToTranslate);
            		if(lfToSQLConfig.isWriteReport())
            			listOfNotTransformedCategories.add(classToTranslate);
            		continue;
            	}
            	
            	Set<OWLClassExpression> eces = classToTranslate.getEquivalentClasses(activeOntology);
            	if(eces.size()>1)
            	{
            		log.warn("Transformation cancelled - cause: more than one definition of category: "+classToTranslate);
            		if(lfToSQLConfig.isWriteReport())
            			listOfNotTransformedCategories.add(classToTranslate);
            		continue;
            	}
            	
            	for(OWLClassExpression ece : eces)
            	{
            		String basicSql;
            		Integer backwardValidity = null;
            		Integer forwardValidity = null;
            		
            		ManchesterOWLSyntaxOWLObjectRendererImpl renderer = new ManchesterOWLSyntaxOWLObjectRendererImpl();
            		String expressionToTransform = EventRendering.transformRendering(renderer.render(ece));
            		
            		if(expressionToTransform.contains("occurs_") || expressionToTransform.contains("(occurs_"))
	            		try 
	            		{
	            			TemporalExpressionAnalyzer a = new TemporalExpressionAnalyzer(expressionToTransform);
	            			backwardValidity = a.determineBackwardValidityOfAnExpresion();
	            			forwardValidity = a.determineForwardValidityOfAnExpresion();
	            		}
	            		catch (Exception e1) 
	            		{
	            			log.error("Problem during transformation of "+classToTranslate+" - unable to analyze following definition: "+ece, e1);
	            			if(lfToSQLConfig.isWriteReport())
	            				listOfNotTransformedCategories.add(classToTranslate);
	            			continue;
	            		}
            		else
            			log.info("Current expression does not contain any temporal relations");
            		
					try 
					{
						
						basicSql = et.transformOWLClassExpression(expressionToTransform);
						if(lfToSQLConfig.isWriteReport())
	            			listOfSuccesfullyTransformedCategories.add(classToTranslate);
					} 
					catch (OwlToSqlTransformationException e1) 
					{
						log.error("Problem during transformation of "+classToTranslate+" - unable to transform following definition: "+ece, e1);
						if(lfToSQLConfig.isWriteReport())
	            			listOfNotTransformedCategories.add(classToTranslate);
						continue;
					}
					
					if(backwardValidity != null)
            		{
            			for(OWLAnnotationAssertionAxiom anAxiom : classToTranslate.getAnnotationAssertionAxioms(activeOntology))
                		{
                			if(anAxiom.getProperty().equals(backwardValidityAP))
                				activeOntology.getOWLOntologyManager().removeAxiom(activeOntology, anAxiom);
                		}
            			activeOntology.getOWLOntologyManager().applyChange(new AddAxiom(activeOntology, getOWLDataFactory().getOWLAnnotationAssertionAxiom(classToTranslate.getIRI(), getOWLDataFactory().getOWLAnnotation(backwardValidityAP, getOWLDataFactory().getOWLLiteral(backwardValidity)))));
            		}
            		if(forwardValidity != null)
            		{
            			for(OWLAnnotationAssertionAxiom anAxiom : classToTranslate.getAnnotationAssertionAxioms(activeOntology))
                		{
                			if(anAxiom.getProperty().equals(forwardValidityAP))
                				activeOntology.getOWLOntologyManager().removeAxiom(activeOntology, anAxiom);
                		}
            			activeOntology.getOWLOntologyManager().applyChange(new AddAxiom(activeOntology, getOWLDataFactory().getOWLAnnotationAssertionAxiom(classToTranslate.getIRI(), getOWLDataFactory().getOWLAnnotation(forwardValidityAP, getOWLDataFactory().getOWLLiteral(forwardValidity)))));
            		}
            		
            		for(OWLAnnotationAssertionAxiom anAxiom : classToTranslate.getAnnotationAssertionAxioms(activeOntology))
            		{
            			if(anAxiom.getProperty().equals(sqlSelectAP))
            				activeOntology.getOWLOntologyManager().removeAxiom(activeOntology, anAxiom);
            		}
            		activeOntology.getOWLOntologyManager().applyChange(new AddAxiom(activeOntology, getOWLDataFactory().getOWLAnnotationAssertionAxiom(classToTranslate.getIRI(), getOWLDataFactory().getOWLAnnotation(sqlSelectAP, getOWLDataFactory().getOWLLiteral(basicSql)))));
            		activeOntology.getOWLOntologyManager().applyChange(new AddAxiom(activeOntology, getOWLDataFactory().getOWLAnnotationAssertionAxiom(classToTranslate.getIRI(), getOWLDataFactory().getOWLAnnotation(getOWLDataFactory().getRDFSComment(), getOWLDataFactory().getOWLLiteral("An SQL script was generated by EventsToSQLP4Transformer on "+new java.util.Date()+".")))));
            	}
            }
            if(lfToSQLConfig.isWriteReport())
            {
            	File reportFile = new File(jarDirectory.concat(File.separator).concat(activeOntology.getOntologyID().getOntologyIRI().getFragment().replace(".owl", "")).concat(".rep"));
            	BufferedWriter fw = null;
        		try 
        		{
        			fw = new BufferedWriter(new FileWriter(reportFile));
        			fw.write("# OwlToSql report "+new java.util.Date());
        			fw.newLine();
        			fw.write("# OWL file: "+activeOntology.getOntologyID().getOntologyIRI());
        			fw.newLine();
        			fw.write("# Number of succesfully transformed categories: "+listOfSuccesfullyTransformedCategories.size());
        			fw.newLine();
        			fw.write("# Number of not transformed categories: "+listOfNotTransformedCategories.size());
        			fw.newLine();
        			fw.write("# List of succesfully transformed categories (name/IRI/sql/list of superclasses): ");
        			fw.newLine();
        			
        			for(int i=0;i<listOfSuccesfullyTransformedCategories.size();i++)
        			{
        				OWLClass currentClass = listOfSuccesfullyTransformedCategories.get(i);
        				String categoryName = currentClass.getIRI().getFragment();
        				String categoryIRI = currentClass.getIRI().toString();
        				String sSuperclasses = "";
        				for(OWLClass cl : reasoner.getSuperClasses(currentClass, false).getFlattened())
        					sSuperclasses = sSuperclasses.concat(cl.getIRI().toString()).concat(";");
        				String sql = null;
        				for(OWLAnnotation ann : currentClass.getAnnotations(activeOntology))
                		{
                			if(ann.getProperty().equals(sqlSelectAP))
                			{
                				if(sql==null)
                					sql = ann.getValue().toString().replace("^^xsd:string", "");
                				else
                				{
                					log.error("There is more than one SQL query for class: "+currentClass);
                					sql = "ERROR! There is more than one SQL query for this category!";
                					break;
                				}
                			}
                		}
        				
        				fw.write(categoryName + reportFileSeparator + categoryIRI + reportFileSeparator + sql + reportFileSeparator + sSuperclasses);
        				fw.newLine();
        				if(i%10000==0)
        					fw.flush();
        			}
        			
        			if(listOfNotTransformedCategories.size()>0)
        			{
        				fw.write("# List of not transformed categories (name/IRI): ");
        				fw.newLine();
        				for(int i=0;i<listOfNotTransformedCategories.size();i++)
        				{
        					OWLClass currentClass = listOfNotTransformedCategories.get(i);
        					String categoryName = currentClass.getIRI().getFragment();
        					String categoryIRI = currentClass.getIRI().toString();
        					fw.write(categoryName + reportFileSeparator + categoryIRI);
        					fw.newLine();
        					if(i%10000==0)
        						fw.flush();
        				}
        			}
        		} 
        		catch (IOException ioe)
        		{
        			log.error("IO Exception while preparing report file", ioe);
				} 
        		finally
        		{	
        			if(fw != null)
						try 
        				{
							fw.close();
						} 
        				catch (IOException e1) 
						{
		        			log.error("IO Exception while preparing report file", e1);
						}
        		}
            }
        }
		
	}

}
